import hid  # 用于USB HID设备通信
from threading import Thread, Lock, Event
from enum import Enum
from queue import Queue
import time
from typing import List, Optional, Union


class StatusType(Enum):
    """
    设备状态枚举类
    CONNECT_OK: 连接成功
    CONNECT_FAIL: 连接失败
    DISCONNECT_OK: 断开连接成功
    DISCONNECT_EXCEPT: 断开连接异常
    """
    CONNECT_OK = 1
    CONNECT_FAIL = 2
    DISCONNECT_OK = 3
    DISCONNECT_EXCEPT = 4


class RFIDHelper:
    """
    RFID读写器操作助手类 (单例模式)

    主要功能：
    - USB设备连接管理
    - 标签读取操作
    - 标签写入操作
    - 设备状态监控
    """
    _instance = None
    _initialized = False

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(RFIDHelper, cls).__new__(cls)
        return cls._instance

    # USB设备常量
    USB_VID = 0x04D8  # Vendor ID
    USB_PID = 0x033F  # Product ID

    # 协议常量
    SOI = 0x7C        # 数据包起始标识
    DEFAULT_TIMEOUT = 1.0  # 默认超时时间(秒)
    MAX_PACKET_SIZE = 64   # 最大数据包大小
    REPORT_SIZE = 65       # USB报告大小

    # 命令常量
    CMD_CONNECT = 0x81     # 连接命令
    CMD_READ_TAG = 0x20    # 读标签命令
    CMD_WRITE_TAG = 0x22   # 写标签命令
    CMD_SET_SELECT = 0x2D  # 设置选择命令

    # 响应常量
    RESP_SUCCESS = 0x00    # 成功响应码
    RESP_START = 0xCC      # 响应起始标识

    def __init__(self):
        # 确保初始化代码只运行一次
        if not RFIDHelper._initialized:
            self.connected = False
            self.connection = None
            self.device_type = None  # 只保留'usb'类型
            self.current_command = None

            # 设置默认地址为FFFF（高位在后，低位在前）
            self.reader_address_low = 0xFF  # 低位
            self.reader_address_high = 0xFF  # 高位

            # 线程同步
            self._lock = Lock()
            self._stop_event = Event()
            self._receive_thread = None
            self._response_queue = Queue()

            # 事件回调
            self._status_callbacks = []
            self._data_callbacks = []

            RFIDHelper._initialized = True

    def get_usb_devices(self) -> List[dict]:
        """
        获取可用的USB设备列表

        Returns:
            List[dict]: 包含设备信息的字典列表
        """
        try:
            unique_devices = {}

            for device in hid.enumerate(self.USB_VID, self.USB_PID):
                device_id = (
                    device['vendor_id'],
                    device['product_id'],
                    device.get('serial_number', '')
                )

                if device_id in unique_devices:
                    continue

                hid_type = "110" if device.get(
                    'product_string', '').find('110') != -1 else "300"

                device_info = {
                    'key': f"HID_{hid_type}_VID_{device['vendor_id']:04X}&PID_{device['product_id']:04X}",
                    'value': device['path'],
                    'path': device['path'],
                    'vendor_id': device['vendor_id'],
                    'product_id': device['product_id'],
                    'serial_number': device.get('serial_number', 'N/A'),
                    'manufacturer_string': device.get('manufacturer_string', 'N/A'),
                    'product_string': device.get('product_string', 'N/A')
                }

                unique_devices[device_id] = device_info

                print("发现设备:")
                print(f"  - 标识符: {device_info['key']}")
                print(f"  - 路径: {device_info['path']}")
                print(f"  - 产品: {device_info['product_string']}")
                print(f"  - 制造商: {device_info['manufacturer_string']}")

            return list(unique_devices.values())

        except Exception as e:
            print(f"获取USB设备列表失败: {e}")
            return []

    def connect_usb(self, device_info: dict) -> bool:
        """连接USB设备"""
        try:
            print("\n=== 开始连接USB设备 ===")
            self.disconnect()

            self.connection = hid.device()
            self.connection.open_path(device_info['path'])
            self.connection.set_nonblocking(1)
            self.device_type = 'usb'

            self._stop_event.clear()
            self._receive_thread = Thread(
                target=self._receive_loop, daemon=True)
            self._receive_thread.start()
            time.sleep(0.2)

            # 发送设备连接命令
            cmd_packet = self._build_packet(self.CMD_CONNECT, 0x32)
            self._send_packet(cmd_packet)

            response = self._wait_response(timeout=1.5)
            if response and len(
                    response) >= 5 and response[3] == self.CMD_CONNECT and response[4] == self.RESP_SUCCESS:
                self.connected = True
                self._notify_status(StatusType.CONNECT_OK, device_info)
                return True

            raise Exception("设备无响应")

        except Exception as e:
            print(f"\n连接失败: {str(e)}")
            self.disconnect()
            self._notify_status(StatusType.CONNECT_FAIL, str(e))
            return False

    def disconnect(self):
        """断开连接"""
        try:
            if self._receive_thread and self._receive_thread.is_alive():
                self._stop_event.set()
                self._receive_thread.join(timeout=1.0)

            if self.connection:
                self.connection.close()

            self.connected = False
            self.connection = None
            self.device_type = None

            while not self._response_queue.empty():
                self._response_queue.get_nowait()

            self._notify_status(StatusType.DISCONNECT_OK, "断开连接成功")

        except Exception as e:
            print(f"断开连接时出错: {e}")
            self._notify_status(StatusType.DISCONNECT_EXCEPT, str(e))

    def read_tag(self) -> Optional[dict]:
        """读取标签EPC"""
        if not self.connected:
            return None

        try:
            cmd_packet = self._build_packet(self.CMD_READ_TAG, 0x00)
            if not self._send_packet(cmd_packet):
                return None

            response = self._read_response(timeout=1.0)
            if response and len(response) >= 9:
                ant_id = response[6]
                pc = response[7:9]
                epc_data = response[9:13]  # 只读取4字节（8位十六进制）
                rssi = response[21] if len(response) > 21 else 0

                result = {
                    "antenna": f"{ant_id:02X}",
                    "pc": f"{pc[0]:02X}{pc[1]:02X}",
                    "epc": ''.join([f'{b:02X}' for b in epc_data]),
                    "rssi": f"{rssi:02X}",
                }
                return result

        except Exception as e:
            print(f"读卡错误: {e}")
            return None

    def write_tag(self, epc_data: str, is_hex: bool = True) -> dict:
        """
        写入EPC标签数据

        Args:
            epc_data (str): 要写入的EPC数据
            is_hex (bool): 输入数据是否为十六进制格式，默认为True

        Returns:
            dict: 写入结果
        """
        try:
            formatted_epc = self.format_epc_data(epc_data, is_hex)
            return self._write_tag_internal(formatted_epc)

        except ValueError as e:
            return {
                "success": False,
                "message": str(e)
            }
        except Exception as e:
            return {
                "success": False,
                "message": f"写卡过程出错: {e}"
            }

    def _build_packet(
            self, cid1: int, cid2_or_info: Union[int, List[int]] = None, info: List[int] = None) -> bytes:
        """构建命令数据包"""
        if isinstance(cid2_or_info, list):
            cid2 = 0
            info = cid2_or_info
        else:
            cid2 = cid2_or_info if cid2_or_info is not None else 0
            info = info if info is not None else []

        info_length = len(info)

        packet = [
            self.SOI,                    # SOI
            self.reader_address_low,      # ADR_L
            self.reader_address_high,     # ADR_H
            cid1,                        # CID1
            cid2,                        # CID2
            info_length,                 # LENGTH
            *info                        # INFO
        ]

        checksum = 0
        for b in packet:
            checksum = (checksum + b) & 0xFF
        checksum = (~checksum & 0xFF) + 1
        if checksum > 0xFF:
            checksum = checksum & 0xFF

        packet.append(checksum)
        return bytes(packet)

    def _send_packet(self, packet: bytes) -> bool:
        """发送数据包"""
        try:
            if len(packet) > 3:
                self.current_command = packet[3]

            if self.device_type == 'usb':
                report = bytearray([0] * self.REPORT_SIZE)
                report[0] = 0x00
                report[1] = len(packet)

                for i, b in enumerate(packet):
                    if i < 63:
                        report[i + 2] = b

                retry_count = 3
                for i in range(retry_count):
                    try:
                        self.connection.write(bytes(report))
                        time.sleep(0.2)
                        return True
                    except Exception as e:
                        print(f"第 {i+1} 次发送失败: {e}")
                        if i < retry_count - 1:
                            time.sleep(0.3)
                            continue
                        raise
            return False

        except Exception as e:
            print(f"发送数据包错误: {e}")
            return False

    def _read_response(self, timeout: float = 1.0) -> Optional[bytes]:
        """等待并读取响应数据"""
        try:
            start_time = time.time()

            while (time.time() - start_time) < timeout:
                try:
                    response = self._response_queue.get(timeout=0.1)
                    if response:
                        if len(response) >= 4:
                            cmd_type = response[3]
                            if cmd_type != self.current_command:
                                continue
                        return response
                except Queue.Empty:
                    continue

            return None

        except Exception as e:
            print(f"读取响应错误: {e}")
            return None

    def _receive_loop(self):
        """接收数据循环"""
        while not self._stop_event.is_set():
            try:
                if self.device_type == 'usb' and self.connection:
                    data = self.connection.read(self.MAX_PACKET_SIZE)
                    if data:
                        start_idx = 1
                        while start_idx < len(data) and data[start_idx] == 0:
                            start_idx += 1

                        end_idx = len(data) - 1
                        while end_idx > start_idx and data[end_idx] == 0:
                            end_idx -= 1

                        if end_idx >= start_idx:
                            valid_data = data[start_idx:end_idx + 1]
                            if valid_data[0] == self.RESP_START:
                                self._response_queue.put(bytes(valid_data))

            except Exception as e:
                print(f"接收线程错误: {e}")
            time.sleep(0.001)

    def _wait_response(self, timeout: float = 1.0) -> Optional[bytes]:
        """等待响应数据"""
        try:
            start_time = time.time()

            while (time.time() - start_time) < timeout:
                try:
                    response = self._response_queue.get(timeout=0.1)
                    if response:
                        return response
                except Queue.Empty:
                    continue
                except Exception as e:
                    print(f"等待响应时出错: {e}")
                    continue

            return None

        except Exception as e:
            print(f"等待响应错误: {e}")
            return None

    def format_epc_data(self, epc_data: str, is_hex: bool = True) -> str:
        """
        格式化EPC数据

        Args:
            epc_data (str): 要格式化的EPC数据
            is_hex (bool): 输入数据是否为十六进制格式，默认为True

        Returns:
            str: 格式化后的十六进制字符串，固定长度为8位
        """
        try:
            if is_hex:
                # 如果输入是十六进制字符串，直接处理
                hex_data = epc_data.upper()
                # 移除可能存在的0x前缀
                hex_data = hex_data.replace('0x', '')
                # 验证是否为有效的十六进制字符串
                int(hex_data, 16)
            else:
                # 如果输入是十进制数字字符串
                if epc_data.isdigit():
                    number = int(epc_data)
                    hex_data = f"{number:X}"  # 不限制位数
                else:
                    # 如果是普通字符串，转换每个字符为十六进制
                    hex_data = ''.join([f"{ord(c):02X}" for c in epc_data])

            # 确保长度为8位
            if len(hex_data) > 8:
                # 超过8位的，取左边8位
                hex_data = hex_data[:8]
            else:
                # 不足8位的，右边补0
                hex_data = hex_data.ljust(8, '0')

            return hex_data

        except Exception as e:
            raise ValueError(f"数据转换错误: {str(e)}")

    def _write_tag_internal(self, epc_data: str) -> dict:
        """内部写卡实现"""
        result = {
            "success": False,
            "message": "",
            "response": None
        }

        try:
            # 1. 读取当前标签
            cmd_packet = self._build_packet(self.CMD_READ_TAG, 0x00)
            if not self._send_packet(cmd_packet):
                result["message"] = "发送读卡命令失败"
                return result

            response = self._read_response(timeout=1.0)
            if not response or len(response) < 9:
                result["message"] = "未检测到标签"
                return result

            # 2. 设置匹配EPC状态
            current_epc = response[9:13]  # 只匹配前4字节
            match_data = [
                0x02,            # MODE
                4,               # LEN - 改为4字节长度
                *current_epc     # EPC
            ]

            cmd_packet = self._build_packet(self.CMD_SET_SELECT, 0x00, match_data)
            if not self._send_packet(cmd_packet):
                result["message"] = "发送匹配命令失败"
                return result

            response = self._read_response(timeout=1.0)
            if not response or response[4] != self.RESP_SUCCESS:
                result["message"] = "设置匹配状态失败"
                return result

            # 3. 写入EPC数据
            info_data = [
                0x00, 0x00, 0x00, 0x00,  # AP
                0x01,                     # MB
                0x02,                     # SA
                0x02,                     # DL - 改为写入4字节数据
            ]

            # 将8位十六进制字符串转换为4字节数据
            epc_bytes = bytes.fromhex(epc_data)
            if len(epc_bytes) != 4:
                raise ValueError(f"数据长度错误: 期望4字节，实际{len(epc_bytes)}字节")

            info_data.extend(epc_bytes)
            cmd_packet = self._build_packet(self.CMD_WRITE_TAG, 0x00, info_data)
            if not self._send_packet(cmd_packet):
                result["message"] = "发送写卡命令失败"
                return result

            response = self._read_response(timeout=2.0)
            if not response:
                result["message"] = "写卡超时"
                return result

            if response[4] == self.RESP_SUCCESS:
                result["success"] = True
                result["message"] = "写卡成功"
            else:
                result["message"] = f"写卡失败，错误码: {response[4]:02X}H"

            result["response"] = response
            return result

        except Exception as e:
            result["message"] = f"写卡过程出错: {e}"
            return result

    def _notify_status(self, status: StatusType, message: str):
        """通知状态变化"""
        for callback in self._status_callbacks:
            try:
                callback(status, message)
            except Exception as e:
                print(f"状态回调执行错误: {e}")
